package com.allwees.bs.c.common.auth.oauth.apple;

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.SigningKeyNotFoundException;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.allwees.bs.c.common.auth.AuthHandler;
import com.allwees.bs.c.common.auth.AuthResult;
import com.allwees.bs.c.common.auth.constant.EAuthType;
import com.allwees.bs.c.common.auth.model.OAuthUserInfo;
import com.allwees.bs.c.module.user.entity.UserEntity;
import com.allwees.bs.c.module.user.req.ThirdPartLoginReq;
import com.allwees.bs.core.modelbase.constant.ResultCode;
import com.allwees.bs.core.modelbase.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.security.interfaces.RSAPublicKey;
import java.util.List;
import java.util.Map;

/**
 *
 */
@Slf4j
@Component
public class AppleAuthHandler extends AuthHandler {

	private static final String URL_APPLE_AUTH_KEYS = "https://appleid.apple.com/auth/keys";

	@Override
	public EAuthType getAuthType() {
		return EAuthType.APPLE;
	}

	private static List<Jwk> jwks;

	@PostConstruct
	public void init(){
		super.init();
		try {
			jwks = new UrlJwkProvider(URL_APPLE_AUTH_KEYS).getAll();
		} catch (SigningKeyNotFoundException e) {
			log.error("Query apple auth keys fail.",e);
			throw new RuntimeException("Query apple auth keys fail.");
		}
	}


	@Override
	public AuthResult auth(ThirdPartLoginReq loginReq) {
		DecodedJWT jwt = JWT.decode(loginReq.getToken());
		checkJwt(jwt);

		try {
			Jwk jwk = getJwk(jwt.getKeyId());
			Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null);
			algorithm.verify(jwt);
		} catch (SignatureVerificationException | JwkException e){
			throw new BusinessException(ResultCode.APPLE_JWT_VERIFY_FAILED, "Apple JWT Verify Failed.");
		}
		String email = jwt.getClaim("email").asString();
		Map<String, Claim> claims = jwt.getClaims();
		for (Map.Entry<String, Claim> entry : claims.entrySet()) {
			log.info("===>claim name:{},value:{}",entry.getKey(),entry.getValue().asString());
		}
		String id = jwt.getSubject();
		Claim givenName = claims.get("givenName");
		String firstName = givenName == null ? email.substring(0, email.indexOf('@')) : givenName.asString();
		Claim familyName = claims.get("familyName");
		String lastName = familyName == null ? "user" : familyName.toString();
		String avatarUrl = "";
		OAuthUserInfo userInfo = new OAuthUserInfo(loginReq.getSourceType(), getAuthType(), loginReq.getShareCode(), id, email, firstName, lastName, avatarUrl);
		//首选email
		UserEntity user = null;
		boolean updated = false;
		if(StringUtils.isNotBlank(email)){
			user = userDao.findByEmail(email);
		}
		if(user == null){
			user = userDao.findByAppleId(id);
		} else {
			if(StringUtils.isBlank(user.getAppleId())){
				user.setAppleId(id);
				updated = true;
			}
			updated = updated || super.completeUserInfo(user, email, firstName, lastName, avatarUrl);
		}
		return auth(user, userInfo, updated);
	}

	private void checkJwt(DecodedJWT jwt){
		if(!jwt.getIssuer().contains("https://appleid.apple.com")){
			throw new BusinessException(ResultCode.APPLE_ILLEGAL_ISSUER, "Issuer Is Not Apple official");
		}

		if(jwt.getExpiresAt().getTime() <= System.currentTimeMillis()){
			throw new BusinessException(ResultCode.APPLE_JWT_EXPIRED, "Identity Token Expired");
		}

		String audience = jwt.getAudience().get(0);
		if(!(audience.contains("com.allwees.www") || audience.contains("com.api.allwees"))){
			throw new BusinessException(ResultCode.APPLE_ILLEGAL_AUDIENCE, "Audience is not for allwees");
		}
//		assert(jwt.getSubject() == request.identifier, "identifier invalid")
	}

	private static Jwk getJwk(String keyId){
		if (keyId == null && jwks.size() == 1) {
			return jwks.get(0);
		}
		if (keyId != null) {
			for (Jwk jwk : jwks) {
				if (keyId.equals(jwk.getId())) {
					return jwk;
				}
			}
		}
		throw new BusinessException(ResultCode.APPLE_KIDS_NOT_FOUND,"No key found in " + URL_APPLE_AUTH_KEYS + " with kid " + keyId);
	}
}
